package org.study.mybatisplus.dynamic.boot.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.osgi.framework.ServiceException;
import org.springframework.web.bind.annotation.*;
import org.study.mybatisplus.dynamic.boot.config.SystemConstant;
import org.study.mybatisplus.dynamic.boot.dto.BrokerMessageDTO;
import org.study.mybatisplus.dynamic.boot.mapper.IotDeviceMapper;
import org.study.mybatisplus.dynamic.boot.mapper.IotDevicePropertyMapper;
import org.study.mybatisplus.dynamic.boot.model.IotDevice;
import org.study.mybatisplus.dynamic.boot.model.IotDeviceProperty;
import org.study.mybatisplus.dynamic.boot.service.CollectorService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/collector")
public class CollectorController {
    private final IotDeviceMapper iotDeviceMapper;
    private final IotDevicePropertyMapper iotDevicePropertyMapper;
    private final CollectorService collectorService;

    /**
     * 将Redis中的采集数据存到时序数据库中
     */
    @GetMapping("/saveData")
    public Object saveData() {
        List<IotDevice> list = iotDeviceMapper.selectList(
                Wrappers.<IotDevice>query().lambda()
                        .eq(IotDevice::getIsDeleted, SystemConstant.DB_NOT_DELETED)
        );
        List<IotDevice> deviceList = list.stream()
                .collect(Collectors.collectingAndThen(
                                Collectors.toMap(device -> device.getGatewayNo() + device.getDeviceNo(),
                                        device -> device,
                                        (existing, replacement) -> replacement),
                                map -> new ArrayList<>(map.values())
                        )
                );
        for (IotDevice iotDevice : deviceList) {
            String gatewayNo = iotDevice.getGatewayNo();
            String deviceNo = iotDevice.getDeviceNo();
            if (StringUtils.isNotBlank(gatewayNo) && StringUtils.isNotBlank(deviceNo)) {
                Map<String, Object> mergeMap = Maps.newHashMap();
                BrokerMessageDTO brokerMessageDTO = new BrokerMessageDTO();
                brokerMessageDTO.setId(gatewayNo);
                brokerMessageDTO.setSn(deviceNo);
                /*Map<Object, Object> cachedMap = stringRedisTemplate.opsForHash().entries("iot:data:" + gatewayNo + ":" + deviceNo);
                if (MapUtils.isNotEmpty(cachedMap)) {
                    cachedMap.forEach((k, v) -> mergeMap.put((String) k, v));
                }*/
                if (MapUtils.isNotEmpty(mergeMap)) {
                    brokerMessageDTO.setDt(mergeMap);
                    collectorService.processMessage(DateUtil.now(), brokerMessageDTO);
                }
            }
        }
        return "操作成功";
    }

    /**
     * 使用mqtt客户端模拟数据上报
     */
    @GetMapping("/mqtt/simulator1")
    public Object mqttSimulator1() {
        List<IotDevice> iotDeviceList = iotDeviceMapper.selectList(
                Wrappers.<IotDevice>query().lambda()
                        .eq(IotDevice::getIsDeleted, SystemConstant.DB_NOT_DELETED)
        );
        if (CollectionUtils.isEmpty(iotDeviceList)) {
            throw new ServiceException("暂无设备信息，请创建设备并添加采集点位后再试！");
        }
        iotDeviceList.forEach(iotDevice -> this.simulator(iotDevice));
        return "操作成功";
    }

    /**
     * 使用mqtt客户端模拟数据上报
     */
    @GetMapping("/mqtt/simulator2")
    public Object mqttSimulator2() {
        String mockDataKey = "data:mock";
        /*if (stringRedisTemplate.hasKey(mockDataKey)) {
            throw new ServiceException("数据模拟程序运行中，请1分钟后再试！");
        }*/
        List<IotDevice> iotDeviceList = iotDeviceMapper.selectList(
                Wrappers.<IotDevice>query().lambda()
                        .eq(IotDevice::getIsDeleted, SystemConstant.DB_NOT_DELETED)
        );
        if (CollectionUtils.isEmpty(iotDeviceList)) {
            throw new ServiceException("暂无设备信息，请创建设备并添加采集点位后再试！");
        }
        // stringRedisTemplate.opsForValue().set(mockDataKey, DateUtil.now(), 1, TimeUnit.MINUTES);
        // 创建一个单线程调度池，任务跑完就 shutdown
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread thread = new Thread(r, "mqtt-simulator");
            // 非守护线程，保证 JVM 不退出
            thread.setDaemon(false);
            return thread;
        });
        scheduler.scheduleAtFixedRate(() -> {
            /*if (!stringRedisTemplate.hasKey(mockDataKey)) {
                log.info("本次数据模拟任务完成，关闭线程池");
                scheduler.shutdown();
            }*/
            iotDeviceList.forEach(iotDevice -> this.simulator(iotDevice));
        }, 0, 3, TimeUnit.MINUTES);
        return "操作成功";
    }

    private void simulator(IotDevice iotDevice) {
        String gatewayNo = iotDevice.getGatewayNo();
        String deviceNo = iotDevice.getDeviceNo();
        List<IotDeviceProperty> iotDevicePropertyList = iotDevicePropertyMapper.selectList(
                Wrappers.<IotDeviceProperty>query().lambda()
                        .eq(IotDeviceProperty::getDeviceId, iotDevice.getId())
                        .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
        );
        if (CollectionUtils.isNotEmpty(iotDevicePropertyList)) {
            if (StringUtils.isNotBlank(gatewayNo) && StringUtils.isNotBlank(deviceNo)) {
                log.info("为设备：{}，{}，{}，{} 生成模拟数据", iotDevice.getId(), iotDevice.getDeviceName(), gatewayNo, deviceNo);
                Map<String, Object> dataMap = Maps.newHashMap();
                iotDevicePropertyList.forEach(deviceInstanceProperty -> {
                    Object value = switch (deviceInstanceProperty.getPropertyType()) {
                        case "1", "TIMESTAMP", "时间戳" -> System.currentTimeMillis();
                        case "2", "INT", "整型" -> RandomUtil.randomInt(0, 1000);
                        case "4", "BIGINT", "长整型" -> RandomUtil.randomLong(0, 1000000);
                        case "6", "FLOAT", "单精度浮点型" -> RandomUtil.randomFloat(0F, 999.99F);
                        case "7", "DOUBLE", "双精度浮点型" -> RandomUtil.randomDouble(0, 999999.99, 6, RoundingMode.HALF_UP);
                        case "8", "BINARY", "单字节字符串" -> RandomUtil.randomString(15);
                        case "9", "SMALLINT", "短整型" -> RandomUtil.randomInt(-32768, 32768);
                        case "11", "TINYINT", "单字节整型" -> RandomUtil.randomInt(-128, 127);
                        case "13", "BOOL", "布尔型" -> RandomUtil.randomBoolean();
                        case "14", "NCHAR", "多字节字符串" -> "测试字符串数据，当前随机汉字：" + RandomUtil.randomChinese();
                        case "19", "DECIMAL", "高精度数值型" -> RandomUtil.randomBigDecimal(BigDecimal.ZERO, BigDecimal.valueOf(999999L, 6));
                        default -> "测试字符串，测试字符串，测试字符串，测试字符串，测试字符串";
                    };
                    dataMap.put(deviceInstanceProperty.getPropertyNo(), value);
                });
                /*HashMap<String, Object> data = new HashMap<>() {{
                    put("Id", gatewayNo);
                    put("Sn", deviceNo);
                    put("Tp", "data");
                    put("Dt", dataMap);
                }};
                mqttClientTemplate.publish("/iot/up", JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8));*/
                BrokerMessageDTO brokerMessageDTO = new BrokerMessageDTO();
                brokerMessageDTO.setId(gatewayNo);
                brokerMessageDTO.setSn(deviceNo);
                brokerMessageDTO.setDt(dataMap);
                collectorService.processMessage(DateUtil.now(), brokerMessageDTO);
            }
        }
    }
}
